package com.springboot2.shiro.configuration;

import com.springboot2.shiro.auth.bind.method.LoginAccountMethodArgumentResolver;
import com.springboot2.shiro.comm.entity.RedisOptions;
import com.springboot2.shiro.mgt.credentials.CipherFreeHashedCredentialsMatcher;
import com.springboot2.shiro.mgt.filter.LoginAccountFilter;
import com.springboot2.shiro.mgt.realm.ShiroJdbcRealm;
import com.springboot2.shiro.mgt.session.CustomWebSessionManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * ShiroConfiguration
 *
 * @author Archx[archx@foxmail.com]
 * at 2018/7/13 20:56
 */
@Configuration
public class ShiroConfiguration implements WebMvcConfigurer {
      /*--------------------------------------------------
    |            C U S T O M E   C O N F I G           |
    ===================================================*/

    @Bean(name = "loginAccountMethodArgumentResolver")
    public LoginAccountMethodArgumentResolver loginAccountMethodArgumentResolver() {
        return new LoginAccountMethodArgumentResolver();
    }

    // 自定义注解
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(loginAccountMethodArgumentResolver());
    }

    /*---------------------------------------------
    |            S H I R O  C O N F I G           |
    ==============================================*/

    @Bean("shiroRedisManager")
    public RedisManager redisManager(RedisOptions options) {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(options.getHost());
        redisManager.setPort(options.getPort());
        redisManager.setTimeout(options.getTimeout());
        redisManager.setPassword(options.getPassword());
        return redisManager;
    }

    @Bean("shiroRedisCacheManager")
    public RedisCacheManager cacheManager(RedisManager redisManager) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager);
        redisCacheManager.setExpire(1800);
        return redisCacheManager;
    }

    @Bean
    public RedisSessionDAO redisSessionDAO(RedisManager redisManager) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager);
        return redisSessionDAO;
    }

    @Bean // 自定义限定匹配器
    public CipherFreeHashedCredentialsMatcher retryLimitHashedCredentialsMatcher() {
        CipherFreeHashedCredentialsMatcher hashedCredentialsMatcher = new CipherFreeHashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(2);// 散列两次，md5(md5(""));
        return hashedCredentialsMatcher;
    }

    @Bean // 会话Cookie模板
    public SimpleCookie simpleCookie() {
        SimpleCookie cookie = new SimpleCookie("sid");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(-1);
        return cookie;
    }

    @Bean
    public CookieRememberMeManager cookieRememberMeManager() {
        CookieRememberMeManager manager = new CookieRememberMeManager();

        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(-1);

        manager.setCookie(cookie);
        // KeyGenerator keygen = KeyGenerator.getInstance("AES");SecretKey deskey = keygen.generateKey();System.out.println(Base64.encodeToString(deskey.getEncoded()));
        byte[] decode = Base64.decode("GgZDeS/dKMJgSjo2oVBERQ==");
        manager.setCipherKey(decode);
        return manager;
    }

    @Bean // 自定义会话管理器
    public SessionManager customWebSessionManager(RedisCacheManager cacheManager, SimpleCookie simpleCookie,
                                                  SessionDAO sessionDAO) {
        CustomWebSessionManager sessionManager = new CustomWebSessionManager();

        sessionManager.setGlobalSessionTimeout(1800000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setCacheManager(cacheManager);
        sessionManager.setSessionIdUrlRewritingEnabled(false); // 重定向时去掉SessionId
        sessionManager.setSessionIdCookie(simpleCookie);
        sessionManager.setSessionDAO(sessionDAO);
        return sessionManager;
    }

    @Bean
    public ShiroJdbcRealm shiroJdbcRealm(CipherFreeHashedCredentialsMatcher matcher, RedisCacheManager cacheManager) {
        ShiroJdbcRealm realm = new ShiroJdbcRealm();

        realm.setCredentialsMatcher(matcher);

        realm.setCachingEnabled(true); /* realm.setCachingEnabled(false) 去除缓存 由自定义CacheAspect控制 */

        realm.setAuthenticationCachingEnabled(true);
        realm.setAuthenticationCacheName("authenticationCache");
        realm.setAuthorizationCachingEnabled(true);
        realm.setAuthorizationCacheName("authorizationCache");

        // CacheManager 必须设置在 setAuthorizationCacheName 之后，不然setAuthorizationCacheName设置值不生效
        realm.setCacheManager(cacheManager);
        return realm;
    }

    @Bean // 安全管理器
    public SecurityManager securityManager(SessionManager sessionManager, ShiroJdbcRealm shiroJdbcRealm,
                                           RedisCacheManager cacheManager, CookieRememberMeManager rememberMeManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroJdbcRealm);

        securityManager.setSessionManager(sessionManager);
        securityManager.setCacheManager(cacheManager);
        securityManager.setRememberMeManager(rememberMeManager);

        return securityManager;
    }

    @Bean // 登录账户消息
    public LoginAccountFilter loginAccountFilter() {
        return new LoginAccountFilter();
    }

    @Bean(name = "shiroFilter") // Shiro WEB 过滤器
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, LoginAccountFilter loginAccountFilter) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, Filter> filters = new HashMap<>();

        filters.put("loginAccount", loginAccountFilter);

        shiroFilterFactoryBean.setFilters(filters);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 注意过滤器配置顺序 不能颠倒

        // 基于表单的身份验证过滤器 org.apache.shiro.web.filter.authc.FormAuthenticationFilter
        // filterChainDefinitionMap.put("/login", "authc");
        filterChainDefinitionMap.put("/logout", "logout");

        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/v1/**", "anon");                 // API
        filterChainDefinitionMap.put("/static/**", "anon");             // 静态资源
        filterChainDefinitionMap.put("/unauth/**", "anon");             // 未授权
        filterChainDefinitionMap.put("/login", "anon");                 // 登录接口

        filterChainDefinitionMap.put("/**", "user,loginAccount"); // 访问其他资源时需要登录和强制登出验证

        // shiroFilterFactoryBean.setLoginUrl("/login");           // 登录地址
        // shiroFilterFactoryBean.setSuccessUrl("/index");         // 登录成功
        // shiroFilterFactoryBean.setUnauthorizedUrl("/403");      // 未授权界面

        // 前后分离 登录行为由前端控制
        shiroFilterFactoryBean.setLoginUrl("/unauth"); // 直接返回未授权的信息

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;

    }

    @Bean // 交由Spring代理
    public FilterRegistrationBean delegatingFilterProxy() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();

        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }

    @Bean // Shiro 注解支持
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;

    }
}
